/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package pl.polidea.lab.microlog4e.ddmuilib;



import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;

import pl.polidea.lab.microlog4e.J2meDevice;
import pl.polidea.lab.microlog4e.btserver.BluetoothServerListener;
import pl.polidea.lab.microlog4e.ddmlib.AndroidDebugBridge;

import java.util.ArrayList;

/**
 * A display of both the devices and their clients.
 */
public final class DevicePanel extends Panel implements BluetoothServerListener  {

    private final static String PREFS_COL_NAME_SERIAL = "devicePanel.Col0"; //$NON-NLS-1$
    private final static String PREFS_COL_BT_ADDR_STATE = "devicePanel.Col1"; //$NON-NLS-1$
    private final static String PREFS_COL_MIDLET_NAME = "devicePanel.Col2"; //$NON-NLS-1$

    private final static int DEVICE_COL_SERIAL = 0;
    private final static int DEVICE_COL_BT_ADDR = 1;
    private final static int DEVICE_COL_MIDLET_NAME = 2;

    public final static int ICON_WIDTH = 16;
    
    public final static String ICON_THREAD = "thread.png"; //$NON-NLS-1$
    public final static String ICON_HEAP = "heap.png"; //$NON-NLS-1$
    public final static String ICON_HALT = "halt.png"; //$NON-NLS-1$
    public final static String ICON_GC = "gc.png"; //$NON-NLS-1$

    public final static String TAG = "DevicePanel"; //$NON-NLS-1$
    
    private J2meDevice mCurrentDevice;

    private Table 		mDevicesTable;
    private TableViewer mTableViewer;


    private Image mThreadImage;
    private Image mHeapImage;
    private Image mWaitingImage;
    private Image mDebuggerImage;
    private Image mDebugErrorImage;

    private final ArrayList<IUiSelectionListener> mListeners = new ArrayList<IUiSelectionListener>();
    private final ArrayList<J2meDevice> mDevices = new ArrayList<J2meDevice>();

    private IImageLoader mLoader;

    private class ContentProvider implements IStructuredContentProvider  {

        public Object[] getElements(Object inputElement) {
        	
        	@SuppressWarnings("unchecked")
        	ArrayList<J2meDevice> persons = (ArrayList<J2meDevice>) inputElement;
    		return persons.toArray();

        }

		@Override
		public void dispose() {
		}

		@Override
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		}
    }

    /**
     * A Label Provider for the {@link TreeViewer} in {@link DevicePanel}. It provides
     * labels and images for {@link IDevice} and {@link Client} objects.
     */
    private class LabelProvider implements ITableLabelProvider {

        public Image getColumnImage(Object element, int columnIndex) 
        {
            return null;
        }

        public String getColumnText(Object element, int columnIndex) {

        	if (element instanceof J2meDevice) 
        	{
        		J2meDevice device = (J2meDevice)element;
                switch (columnIndex) {
                    
                case DEVICE_COL_SERIAL:
                        return device.getName();
                        
                    case DEVICE_COL_BT_ADDR:
                        return device.getBtAddress();
                        
                    case DEVICE_COL_MIDLET_NAME: 
                    	return "";
                }
            } 
            return null;
        }

        public void addListener(ILabelProviderListener listener) {
            // pass
        }

        public void dispose() {
            // pass
        }

        public boolean isLabelProperty(Object element, String property) {
            // pass
            return false;
        }

        public void removeListener(ILabelProviderListener listener) {
            // pass
        }
    }

    /**
     * Classes which implement this interface provide methods that deals
     * with {@link IDevice} and {@link Client} selection changes coming from the ui.
     */
    public interface IUiSelectionListener {

        public void selectionChanged(J2meDevice selectedDevice);
    }

 
    public DevicePanel(IImageLoader loader) {
       
    	System.out.println(TAG+" CTR");
    	
    	mLoader = loader;
        
        
    }

    public void addSelectionListener(IUiSelectionListener listener) {
        mListeners.add(listener);
    }

    public void removeSelectionListener(IUiSelectionListener listener) {
        mListeners.remove(listener);
    }

    @Override
    protected Control createControl(Composite parent) {
        loadImages(parent.getDisplay(), mLoader);

        parent.setLayout(new FillLayout());

        // create the tree and its column
        mDevicesTable = new Table(parent, SWT.SINGLE | SWT.FULL_SELECTION);
        mDevicesTable.setHeaderVisible(true);
        mDevicesTable.setLinesVisible(true);

        IPreferenceStore store = DdmUiPreferences.getStore();

        TableHelper.createTableColumn(mDevicesTable, "Name", SWT.LEFT, "com.android.home", PREFS_COL_NAME_SERIAL, store);
        TableHelper.createTableColumn(mDevicesTable, "Address", SWT.LEFT, "Offline", PREFS_COL_BT_ADDR_STATE, store);
        TableHelper.createTableColumn(mDevicesTable, "Midlet", SWT.LEFT, "9999-9999", PREFS_COL_MIDLET_NAME, store);

        // create the tree viewer
        mTableViewer = new TableViewer(mDevicesTable);


        // set up the content and label providers.
        mTableViewer.setContentProvider(new ContentProvider());
        mTableViewer.setLabelProvider(new LabelProvider());

        mDevicesTable.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                notifyListeners();
            }
        });
        
        mDevices.add(new J2meDevice("test", "address"));
        
        mTableViewer.setInput(mDevices);
       
        
        

        return mDevicesTable;
    }

    /**
     * Sets the focus to the proper control inside the panel.
     */
    @Override
    public void setFocus() {
        mDevicesTable.setFocus();
    }

    @Override
    protected void postCreation() {

    }

    public void dispose() {
    }


    /**
     * Returns the selected {@link IDevice}. If a {@link Client} is selected, it returns the
     * IDevice object containing the client.
     */
    public J2meDevice getSelectedDevice() {
        return mCurrentDevice;
    }

    /**
     * Sent when the a device is connected to the {@link AndroidDebugBridge}.
     * <p/>
     * This is sent from a non UI thread.
     * @param device the new device.
     *
     * @see IDeviceChangeListener#deviceConnected(IDevice)
     */
    public void deviceConnected(String address, String name) {
    	
        exec(new Runnable() {
            public void run() {
                if (mDevicesTable.isDisposed() == false) {
                    // refresh all
                    mTableViewer.refresh();

                    // notify the listener of a possible selection change.
                    notifyListeners();
                } 
              
            }
        });

        synchronized (mDevices) {
                mDevices.add(new J2meDevice(name, address));
        }
        
    }

    /**
     * Sent when the a device is connected to the {@link AndroidDebugBridge}.
     * <p/>
     * This is sent from a non UI thread.
     * @param device the new device.
     *
     * @see IDeviceChangeListener#deviceDisconnected(IDevice)
     */
    public void deviceDisconnected(String address, String name) {
       
    	J2meDevice device = new J2meDevice(name, address);
    	
    	deviceConnected(address, name);

        // just in case, we remove it from the list of devices to expand.
        synchronized (mDevices) {
            mDevices.remove(device);
        }
    }

    /**
     * Sent when a device data changed, or when clients are started/terminated on the device.
     * <p/>
     * This is sent from a non UI thread.
     * @param device the device that was updated.
     * @param changeMask the mask indicating what changed.
     *
     * @see IDeviceChangeListener#deviceChanged(IDevice)
     */
    public void deviceChanged(final J2meDevice device, int changeMask) {
        
        synchronized (mDevices) {
            int index = mDevices.indexOf(device);
            mDevices.remove(index);
        }

      

        exec(new Runnable() {
            public void run() {
                if (mDevicesTable.isDisposed() == false) {
                    // look if the current device is selected. This is done in case the current
                    // client of this particular device was killed. In this case, we'll need to
                    // manually reselect the device.

                	J2meDevice selectedDevice = getSelectedDevice();

                    // refresh the device
                    mTableViewer.refresh(device);

                    // if the selected device was the changed device and the new selection is
                    // empty, we reselect the device.
                    if (selectedDevice == device && mTableViewer.getSelection().isEmpty()) {
                        mTableViewer.setSelection(new TreeSelection(new TreePath(
                                new Object[] { device })));
                    }

                    // notify the listener of a possible selection change.
                    notifyListeners();

                    
                } 
            }
        });
    }

    

    private void loadImages(Display display, IImageLoader loader) {

        if (mThreadImage == null) {
            mThreadImage = ImageHelper.loadImage(loader, display, ICON_THREAD,
                    ICON_WIDTH, ICON_WIDTH,
                    display.getSystemColor(SWT.COLOR_YELLOW));
        }
        if (mHeapImage == null) {
            mHeapImage = ImageHelper.loadImage(loader, display, ICON_HEAP,
                    ICON_WIDTH, ICON_WIDTH,
                    display.getSystemColor(SWT.COLOR_BLUE));
        }
        if (mWaitingImage == null) {
            mWaitingImage = ImageHelper.loadImage(loader, display,
                    "debug-wait.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$
                    display.getSystemColor(SWT.COLOR_RED));
        }
        if (mDebuggerImage == null) {
            mDebuggerImage = ImageHelper.loadImage(loader, display,
                    "debug-attach.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$
                    display.getSystemColor(SWT.COLOR_GREEN));
        }
        if (mDebugErrorImage == null) {
            mDebugErrorImage = ImageHelper.loadImage(loader, display,
                    "debug-error.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$
                    display.getSystemColor(SWT.COLOR_RED));
        }
    }



    /**
     * Executes the {@link Runnable} in the UI thread.
     * @param runnable the runnable to execute.
     */
    private void exec(Runnable runnable) {
        try {
            Display display = mDevicesTable.getDisplay();
            display.asyncExec(runnable);
        } 
        catch (SWTException e) {}
    }

    private void notifyListeners() {
        // get the selection
        TableItem[] items = mDevicesTable.getSelection();


        J2meDevice device = null;

        if (items.length == 1) {
            Object object = items[0].getData();
            
            if (object instanceof J2meDevice) {
                device = (J2meDevice)object;
            }
        }

        notifyListeners(device);
    }

    private void notifyListeners(J2meDevice selectedDevice) {
        if (selectedDevice != mCurrentDevice) {
            mCurrentDevice = selectedDevice;


            for (IUiSelectionListener listener : mListeners) {
                // notify the listener with a try/catch-all to make sure this thread won't die
                // because of an uncaught exception before all the listeners were notified.
                try {
                    listener.selectionChanged(selectedDevice);
                } 
                catch (Exception e) {
                }
            }
        }
    }

	@Override
	public void messageReceived(String message) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void serverStarted(String url) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void shutdown() {
		// TODO Auto-generated method stub
		
	}

}
